package com.example.understandvideo.tool;

import com.example.understandvideo.pojo.MarathonPlayerPoints;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class VideoFrameExtractor {
    private String ffmpegPath = "/usr/local/arcvideo/cloud/ffmpeg/ffmpeg";  // ffmpeg 可执行文件的路径

    public VideoFrameExtractor(String ffmpegPath) {
        this.ffmpegPath = ffmpegPath;
    }

    /**
     * 抽取视频的帧，并保存为图像文件
     *
     * @param videoFilePath   视频文件的路径
     * @param outputDirPath   保存帧图片的输出目录
     * @param framesPerSecond 每秒抽取的帧数
     * @return 返回抽取的图像文件列表
     * @throws IOException
     */
    public List<String> extractFrames(String videoFilePath, String outputDirPath, int framesPerSecond) throws IOException {
        double duration = getVideoDuration(videoFilePath);
        // 输出结果
        if (duration != -1) {
            System.out.println("视频时长: " + duration + "毫秒");
        } else {
            System.out.println("无法获取视频时长。");
            return null;
        }
        List<String> frameFiles = new ArrayList<>();
        File outputDir = new File(outputDirPath);

        // 如果输出目录不存在则创建
        if (!outputDir.exists()) {
            outputDir.mkdirs();  // 创建输出目录
        }

        // 确保输出目录路径是完整的绝对路径
        String absoluteOutputPath = outputDir.getAbsolutePath();
        for (int i = 0; i * framesPerSecond < duration; i++) {
            String targetPath = absoluteOutputPath + "/" + String.format("frame_%04d.png", i);
            String[] command = new String[]{
                    ffmpegPath, "-ss", millisecondsToDurationString(i * 1000), "-i", videoFilePath,
                    "-vf", "scale=120:-1", "-vframes", "1", "-y", targetPath
            };
            System.out.println(String.join(" ", command));
            try {
                Process process = Runtime.getRuntime().exec(command);
                process.waitFor();
                process.destroy();
            } catch (IOException | InterruptedException e) {
                System.out.println("执行命令时发生异常: " + e.getMessage());
                e.printStackTrace();
            }
            if (new File(targetPath).exists()) {
                frameFiles.add(targetPath);
            }
        }

        return frameFiles;
    }

    /**
     * 抽取视频的帧，并保存为图像文件
     *
     * @param videoFilePath   视频文件的路径
     * @param outputDirPath   保存帧图片的输出目录
     * @param framesPerSecond 每秒抽取的帧数
     * @return 返回抽取的图像文件列表
     * @throws IOException
     */
    public List<String> extractFramesV2(String videoFilePath, String outputDirPath, int framesPerSecond) throws IOException {
        System.out.println("getFfmpegExcFileName" + ffmpegPath);

        List<String> frameFiles = new ArrayList<>();
        File outputDir = new File(outputDirPath);

        // 如果输出目录不存在则创建
        if (!outputDir.exists()) {
            outputDir.mkdirs();  // 创建输出目录
        }

        // 确保输出目录路径是完整的绝对路径
        String absoluteOutputPath = outputDir.getAbsolutePath();
        // 示例：每隔5秒抽取一帧
        String[] command = {
                ffmpegPath,
                "-i", videoFilePath,
                "-vf", "fps=" + framesPerSecond + ",scale=120:-1",// 每5秒一帧
//                "-vf", "fps=" + framesPerSecond,// 每5秒一帧
                absoluteOutputPath + File.separator + "frame_%04d.png"
        };
        executeFFmpegCommand(command);
        for (File kid : outputDir.listFiles()) {
            frameFiles.add(kid.getAbsolutePath());
        }
        return frameFiles;
    }

    public List<String> extractFramesV3(String videoFilePath, String outputDirPath, int framesPerSecond) throws IOException {
        System.out.println("getFfmpegExcFileName" + ffmpegPath);

        List<String> frameFiles = new ArrayList<>();
        File outputDir = new File(outputDirPath);

        // 如果输出目录不存在则创建
        if (!outputDir.exists()) {
            outputDir.mkdirs();  // 创建输出目录
        }

        // 确保输出目录路径是完整的绝对路径
        String absoluteOutputPath = outputDir.getAbsolutePath();
        // 示例：每隔5秒抽取一帧
        String[] command = {
                ffmpegPath,
                "-i", videoFilePath,
                "-vf", "fps=" + framesPerSecond,// 每5秒一帧
                absoluteOutputPath + File.separator + "frame_%04d.jpg"
        };
        executeFFmpegCommand(command);
        for (File kid : outputDir.listFiles()) {
            frameFiles.add(kid.getAbsolutePath());
        }
        return frameFiles;
    }

    public List<String> extractFramesV4(String videoFilePath, String outputDirPath, int framesPerSecond) throws IOException {
        System.out.println("getFfmpegExcFileName" + ffmpegPath);

        List<String> frameFiles = new ArrayList<>();
        File outputDir = new File(outputDirPath);

        // 如果输出目录不存在则创建
        if (!outputDir.exists()) {
            outputDir.mkdirs();  // 创建输出目录
        }

        // 确保输出目录路径是完整的绝对路径
        String absoluteOutputPath = outputDir.getAbsolutePath();
        // 示例：每隔5秒抽取一帧
        String[] command = {
                ffmpegPath,
                "-i", videoFilePath,
                "-vf", "fps=" + framesPerSecond,// 每5秒一帧
                absoluteOutputPath + File.separator + "frame_%04d.jpg"
        };
        executeFFmpegCommand(command);
        for (File kid : outputDir.listFiles()) {
            frameFiles.add(kid.getAbsolutePath());
        }
        return frameFiles;
    }

    /**
     * 抽取视频的帧，并保存为图像文件
     *
     * @param videoFilePath   视频文件的路径
     * @param outputFilePath   音频输出路径
     * @return 返回抽取的图像文件列表
     * @throws IOException
     */
    public String extractVoice(String videoFilePath, String outputFilePath) throws IOException {
        System.out.println("getFfmpegExcFileName" + ffmpegPath);
        // 确保输出目录路径是完整的绝对路径
        // 示例：每隔5秒抽取一帧
        String[] command = {
                ffmpegPath,
                "-i", videoFilePath,
                "-vn", "-q:a","0",outputFilePath
        };
        executeFFmpegCommand(command);
        return outputFilePath;
    }


    /**
     * 根据时间点位从视频中提取片段并合并
     */
    public void clipVideo(String inputVideo, String tempDirPath, String outputVideo, List<MarathonPlayerPoints> clipPoints)
            throws IOException, InterruptedException {
        // 创建临时目录
        File tempDir = new File(tempDirPath);
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }

        // 生成每个片段
        for (int i = 0; i < clipPoints.size(); i++) {
            MarathonPlayerPoints point = clipPoints.get(i);
            double startTime = point.getStartTime() / 1000.0;
            double endTime = point.getEndTime() / 1000.0;
            String clipFile = tempDir.getAbsolutePath() + File.separator + "clip_" + i + ".mp4";

            System.out.println("正在处理片段 " + (i + 1) + "/" + clipPoints.size() + "...");
            executeCommand(
                    ffmpegPath, "-y",
                    "-ss", String.valueOf(startTime),
                    "-i", inputVideo,
                    "-to", String.valueOf(endTime),
                    "-c", "copy",
                    clipFile
            );
        }

        // 创建片段列表文件
        File listFile = new File(tempDir, "filelist.txt");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(listFile))) {
            for (int i = 0; i < clipPoints.size(); i++) {
                writer.write("file '" + tempDir.getAbsolutePath() + File.separator + "clip_" + i + ".mp4'\n");
            }
        }

        // 合并所有片段
        System.out.println("正在合并所有片段...");
        executeCommand(
                ffmpegPath, "-y",
                "-f", "concat",
                "-safe", "0",
                "-i", listFile.getAbsolutePath(),
                "-c", "copy",
                outputVideo
        );

        // 清理临时文件
        System.out.println("清理临时文件...");
        for (int i = 0; i < clipPoints.size(); i++) {
            new File(tempDir, "clip_" + i + ".mp4").delete();
        }
        listFile.delete();
        tempDir.delete();
    }

    /**
     * 执行外部命令
     */
    private void executeCommand(String... command) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        Process process = processBuilder.start();

        // 读取标准输出和错误输出
        Thread stdOutThread = new Thread(() -> {
            java.util.Scanner s = new java.util.Scanner(process.getInputStream());
            while (s.hasNextLine()) {
                System.out.println(s.nextLine());
            }
            s.close();
        });

        Thread errOutThread = new Thread(() -> {
            java.util.Scanner s = new java.util.Scanner(process.getErrorStream());
            while (s.hasNextLine()) {
                System.err.println(s.nextLine());
            }
            s.close();
        });

        stdOutThread.start();
        errOutThread.start();

        int exitCode = process.waitFor();
        stdOutThread.join();
        errOutThread.join();

        if (exitCode != 0) {
            throw new IOException("命令执行失败，退出代码: " + exitCode);
        }
    }

    private void executeFFmpegCommand(String[] command) {
        try {
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            Process process = processBuilder.start();

            // 读取错误流（FFmpeg的输出通常通过错误流返回）
            BufferedReader errorReader = new BufferedReader(
                    new InputStreamReader(process.getErrorStream()));

            String line;
            StringBuilder output = new StringBuilder();
            while ((line = errorReader.readLine()) != null) {
                output.append(line).append("\n");
            }

            // 设置超时时间（毫秒）
            if (process.waitFor(60, TimeUnit.SECONDS)) {
                process.destroy();
            }
            errorReader.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 处理输入流和错误流
    private void handleStream(InputStream inputStream) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 可选：打印输出信息
                // System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取视频的时长（以秒为单位）
     *
     * @param videoFilePath 视频文件的路径
     * @return 视频的时长（以秒为单位）
     * @throws IOException
     */
    public double getVideoDuration(String videoFilePath) throws IOException {
        // 定义ffmpeg命令，获取视频时长
        String command = String.format("%s -i %s",
                ffmpegPath, videoFilePath);

        // 执行ffmpeg命令
        Process process = Runtime.getRuntime().exec(command);

        // 读取命令输出
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String line;
        String durationStr = null;
        Pattern pattern = Pattern.compile("Duration: (\\d{2}:\\d{2}:\\d{2}\\.\\d{2})");
        Matcher matcher;

        while ((line = reader.readLine()) != null) {
            matcher = pattern.matcher(line);
            if (matcher.find()) {
                durationStr = matcher.group(1);
                break;
            }
        }
        reader.close();
        // 解析时长
        if (durationStr != null) {
            try {
                return parseDurationToMilliseconds(durationStr);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        return -1; // 返回-1表示获取失败
    }

    /**
     * 将时长字符串（例如 "00:00:22.19"）转换为毫秒数
     *
     * @param durationStr 时长字符串
     * @return 毫秒数
     * @throws NumberFormatException
     */
    private double parseDurationToMilliseconds(String durationStr) throws NumberFormatException {
        // 分割时长字符串
        String[] parts = durationStr.split(":");
        if (parts.length != 3) {
            throw new NumberFormatException("时长格式不正确: " + durationStr);
        }

        // 提取小时、分钟、秒和毫秒
        int hours = Integer.parseInt(parts[0]);
        int minutes = Integer.parseInt(parts[1]);
        String[] secondsParts = parts[2].split("\\.");
        int seconds = Integer.parseInt(secondsParts[0]);
        int milliseconds = secondsParts.length > 1 ? Integer.parseInt(secondsParts[1]) : 0;

        // 转换为毫秒数
        double totalMilliseconds = (hours * 3600 + minutes * 60 + seconds) * 1000 + milliseconds;
        return totalMilliseconds;
    }

    /**
     * 将毫秒数转换为 HH:mm:ss.SS 格式的字符串
     *
     * @param milliseconds 毫秒数
     * @return HH:mm:ss.SS 格式的字符串
     */
    public String millisecondsToDurationString(double milliseconds) {
        int hours = (int) (milliseconds / (1000 * 60 * 60));
        milliseconds -= hours * 1000 * 60 * 60;
        int minutes = (int) (milliseconds / (1000 * 60));
        milliseconds -= minutes * 1000 * 60;
        int seconds = (int) (milliseconds / 1000);
        milliseconds -= seconds * 1000;

        // 格式化为 HH:mm:ss.SS
        return String.format("%02d:%02d.%02d", minutes, seconds, (int) milliseconds);
    }

//    public static void main(String[] args) {
//        try {
//            // 实例化工具类，传入FFmpeg的路径
//            VideoFrameExtractor extractor = new VideoFrameExtractor(ffmpegPath);
//
//            // 设置视频路径和帧输出目录
//            String videoFilePath = "D:\\home\\work\\rivulet-dawn\\2025-04-18\\0000000123076572.mp4";
//            String outputDirPath = "D:\\home\\work\\rivulet-dawn\\thumbnail\\2025-04-18\\test\\";  // 指定保存帧的目录
//
////            double duration = extractor.getVideoDuration(videoFilePath);
////
////            // 输出结果
////            if (duration != -1) {
////                System.out.println("视频时长: " + duration + "毫秒");
////            } else {
////                System.out.println("无法获取视频时长。");
////            }
//
//            // 调用方法抽取帧
//            List<String> frames = extractor.extractFrames(videoFilePath, outputDirPath, 1000);
//
//            // 输出结果
//            System.out.println("共抽取到" + frames.size() + "帧图像，保存在目录：" + outputDirPath);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
}